package com.yupi.yupicturebackend.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yupi.yupicturebackend.mapper.PictureMapper;
import com.yupi.yupicturebackend.model.entity.Picture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class LikeBasedRecommender {

    private static final String PICTURE_LIKED_KEY = "picture:liked:";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private PictureMapper pictureMapper;

    /**
     * 为用户推荐图片
     * @param userId 用户ID
     * @param count 推荐数量
     * @return 推荐图片列表
     */
    public List<Picture> recommendForUser(Long userId, int count, List<String> tags) {
        // 1. 获取当前登录用户点赞过的图片ID列表
        Set<Long> userLikedPictureIds = getUserLikedPictures(userId);

        if (userLikedPictureIds.isEmpty()) {
            return getPopularPictures(count, tags); // 冷启动返回热门图片
        }

        // 2. 找到有相似点赞行为的用户
        Set<Long> similarUsers = findSimilarUsers(userId, userLikedPictureIds);

        // 3. 收集这些用户喜欢但目标用户未点赞的图片
        Set<Long> recommendPictureIds = getRecommendationsFromSimilarUsers(
                userId, userLikedPictureIds, similarUsers);

        // 4. 如果没有推荐结果，退回热门图片
        if (recommendPictureIds.isEmpty()) {
            return getPopularPictures(count, tags);
        }

        // 5. 查询图片详情并设置点赞状态
        return getPicturesWithLikeStatus(recommendPictureIds, userId, count, tags);
    }

    /**
     * 获取用户点赞过的所有图片ID
     */
    private Set<Long> getUserLikedPictures(Long userId) {
        Set<String> keys = stringRedisTemplate.keys(PICTURE_LIKED_KEY + "*");
        // 用户对图片的点赞记录
        Set<Long> likedPictureIds = new HashSet<>();

        if (keys != null) {
            for (String key : keys) {
                // 去查询遍历到的图片id + 当前userID 是否在redis中
                Double score = stringRedisTemplate.opsForZSet()
                        .score(key, userId.toString());
                // 存在 则解析并将图片id添加到集合里
                if (score != null) {
                    Long pictureId = Long.parseLong(key.substring(PICTURE_LIKED_KEY.length()));
                    likedPictureIds.add(pictureId);
                }
            }
        }
        return likedPictureIds;
    }

    /**
     * 查找相似用户（基于共同点赞）
     */
    private Set<Long> findSimilarUsers(Long targetUserId, Set<Long> targetUserLikedPictures) {
        Map<Long, Integer> similarityScores = new HashMap<>();

        for (Long pictureId : targetUserLikedPictures) {
            String key = PICTURE_LIKED_KEY + pictureId;
            // 获取对当前用户点赞的图片 进行点赞的其他用户群体
            Set<String> userIds = stringRedisTemplate.opsForZSet().range(key, 0, -1);

            if (userIds != null) {
                for (String userIdStr : userIds) {
                    Long userId = Long.parseLong(userIdStr);
                    if (userId.equals(targetUserId)) continue;

                    // 计算相似度分值，这里简单的加1
                    similarityScores.merge(userId, 1, Integer::sum);
                    /**
                     merge相当于如下
                     *
                     * if (similarityScores.containsKey(userId)) {
                     *     similarityScores.put(userId, similarityScores.get(userId) + 1);
                     * } else {
                     *     similarityScores.put(userId, 1);
                     * }
                     */
                }
            }
        }

        /**
         * 将similarityScores的条目流化。
         * 按照值（相似度分值）降序排序。
         * 限制结果为前5个条目。
         * 提取键（用户ID）并收集为集合
         */
        return similarityScores.entrySet().stream()
                .sorted(Map.Entry.<Long, Integer>comparingByValue().reversed())
                .limit(5)
                .map(Map.Entry::getKey)
                .collect(Collectors.toSet());
    }

    /**
     * 从相似用户获取推荐图片ID
     */
    private Set<Long> getRecommendationsFromSimilarUsers(
            Long targetUserId,
            Set<Long> targetUserLikedPictures,
            Set<Long> similarUsers) {

        Set<Long> recommendations = new HashSet<>();

        for (Long similarUserId : similarUsers) {
            Set<Long> similarUserLikes = getUserLikedPictures(similarUserId);
            similarUserLikes.stream()
                    .filter(pictureId -> !targetUserLikedPictures.contains(pictureId))
                    .forEach(recommendations::add);
        }

        return recommendations;
    }

    /**
     * 获取热门图片（按点赞数排序）
     */
    public List<Picture> getPopularPictures(int count, List<String> tags) {
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("liked");
        //保证返回的热门图片 都是公共图片 并且是审核已通过的图片
        queryWrapper.eq("reviewStatus", 1);
        queryWrapper.isNull(true, "spaceId");  //ture就是null  false就不用这个条件
        // 添加标签过滤条件
        if (tags != null && !tags.isEmpty()) {
            for (String tag : tags) {
                queryWrapper.like("tags", "\"" + tag + "\"");
            }
        }
        queryWrapper.last("LIMIT " + count);
        return pictureMapper.selectList(queryWrapper);
    }

    /**
     * 带标签过滤的图片查询, 查询图片并设置点赞状态
     */
    private List<Picture> getPicturesWithLikeStatus(Set<Long> pictureIds, Long userId, int count, List<String> tags) {
        // 先查询所有符合条件的图片
        List<Picture> pictures = pictureMapper.selectBatchIds(pictureIds);

        // 如果没有标签过滤条件，直接返回前count个
        if (tags == null || tags.isEmpty()) {
            List<Picture> result = pictures.stream().limit(count).collect(Collectors.toList());
            setLikeStatus(result, userId);
            return result;
        }

        // 在内存中进行标签过滤
        List<Picture> filteredPictures = pictures.stream()
                .filter(pic -> {
                    try {
                        // 假设pic.getTags()返回的是JSON数组格式的字符串，如["校园","风景"]
                        // 或者逗号分隔的字符串，如"校园,风景"
                        String tagsStr = pic.getTags();
                        if (tagsStr == null || tagsStr.trim().isEmpty()) {
                            return false;
                        }

                        // 尝试解析为JSON数组
                        ObjectMapper objectMapper = new ObjectMapper();
                        List<String> picTags;
                        if (tagsStr.startsWith("[")) {
                            // 已经是JSON数组格式
                            picTags = objectMapper.readValue(tagsStr, new TypeReference<List<String>>() {});
                        } else {
                            // 逗号分隔的字符串
                            picTags = Arrays.asList(tagsStr.split(","));
                        }

                        // 检查是否有匹配的标签
                        return picTags != null && !picTags.isEmpty() &&
//                                picTags.stream().anyMatch(tag -> tags.contains(tag.trim()));
                                tags.stream()  // 遍历用户传入的标签
                                        .allMatch(userTag ->  // 每一个 userTag 都要在 picTags 里找到
                                                picTags.stream()
                                                        .anyMatch(picTag -> picTag.equalsIgnoreCase(userTag.trim()))
                                        );
                        /**
                         * anyMatch 是只要匹配任意一个标签即可
                         */
                    } catch (Exception e) {
                        log.error("解析标签失败: {}", pic.getTags(), e);
                        return false;
                    }
                })
                .limit(count)
                .collect(Collectors.toList());

        // 设置点赞状态
        setLikeStatus(filteredPictures, userId);

        return filteredPictures;
    }

    // 提取设置点赞状态的公共方法
    private void setLikeStatus(List<Picture> pictures, Long userId) {
        pictures.forEach(picture -> {
            String key = PICTURE_LIKED_KEY + picture.getId();
            Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
            picture.setIsLike(score != null);
        });
    }
}